package net.lateeye.search;

import java.util.LinkedList;

import net.lateeye.util.ExpressionHandler;

public abstract class AbsSearchResultPageTextProcessor
{
	private final String COMMA = ",";

	/**
	 * @author ISAO Saito <130s@1995.sfc.ne.jp>
	 * @param queryStr
	 *            this will be set to ResultSet instance
	 * @param htmlbody_searchResult
	 * @return LinkedList list of HashMap which contain attributes of each
	 *         result
	 */
	public AbsResultSet createResultSet(String queryStr,
			String htmlbody_searchResult)
	{

		if (htmlbody_searchResult == null) {
			return null;
		}

		AbsResultSet rs = this.instantiateResultSet();
		rs.setProperty(AbsResultSet.PROPKEY_QUERY, queryStr);

		LinkedList<String> results_separated = this
				.extractResults(htmlbody_searchResult);
		for (int i = 0; i < results_separated.size(); i++) {
			String docUrl = null;
			String docTitle_raw = null;
			String docTitle = null;
			String summary = null;

			String result_str = results_separated.get(i);

			SearchResult result_map = this.instantiateSingleSearchResult();

			// 1/4/11. DO) extract metadata, and set them on the search result
			// instance. Notice, the order of metadata processed is important,
			// because URL and the title of the content are 2 intrinsic metadata
			// that cannot be missed to identify a single serch result.
			// Therefore, these 2 metadata are to be processed at first here,
			// and if either or both of them isn't/aren't found/extracted
			// properly, that particular single result will be skipped and move
			// on to the next result.

			try {
				docUrl = this.extractDocUrl(result_str);
			} catch (IndexOutOfBoundsException e) {
				docUrl = "(Not foound.)";
				continue; // Do) Ignore this result and move on to the next
				// result in the resultSet.
			}

			try {
				docTitle_raw = this.extractDocTitle(result_str);
			} catch (IndexOutOfBoundsException e) {
				docTitle_raw = "(Not foound.)";
				continue; // Do) Ignore this result and move on to the next
				// result in the resultSet.
			}
			// DO) Remove tags in the title_raw
			docTitle = ExpressionHandler.removeHtmlTagsSimply(docTitle_raw);

			// DO) Metadata from here CAN miss.
			try {
				summary = this.extractSummary(result_str);
			} catch (IndexOutOfBoundsException e) {
				summary = "(Not foound.)";
			}

			result_map.setOrder(Integer.toString(i));
			result_map.setTitle_Raw(docTitle_raw);
			result_map.setTitle(docTitle);
			result_map.setDocumentURI(docUrl);
			result_map.setDocumentID(docUrl);
			result_map.setDescription(summary);
			result_map = this.extractOtherMetadata(result_str, result_map);

			rs.setResult(result_map);
		}

		long estSize = this.extractEstimatedSize(htmlbody_searchResult);
		rs.setEstimatedNumberOfResultsLong(estSize);

		return rs;
	}

	/**
	 * 
	 * @author Isao Isaac Saito <130s@lateeye.net>
	 * @return Empty instance of a search result instance of each sub-class.
	 */
	protected abstract SearchResult instantiateSingleSearchResult();

	/**
	 * if parse using external parser(ex. ParserCallback), it should better be
	 * called here.
	 * 
	 * @author ISAO Saito <130s@1995.sfc.ne.jp>
	 * @since 2008/04/10
	 * @param result_str
	 *            all string for a result
	 * @param result_map
	 *            result instance
	 * @return Input result_map instance with the necessary value added.
	 */
	protected abstract SearchResult extractOtherMetadata(String result_str,
			SearchResult result_map);

	protected abstract AbsResultSet instantiateResultSet();

	/**
	 * Returns a list of each search result item.
	 * 
	 * @param htmlbody_searchResult
	 * @return LinkedList list of HashMap which contain attributes of each
	 *         result
	 */
	protected abstract LinkedList<String> extractResults(
			String htmlbody_searchResult);

	protected abstract String extractSummary(String htmlbody_searchResult);

	/**
	 * 
	 * @param urlStr
	 * @return
	 * @deprecated 2008jan25/130s/why don't we use java.net.URL#getHost insted?
	 */
	private String extractUrlDomain(String urlStr)
	{
		if (urlStr == null) {
			return null;
		}
		String triggeringString = "://";
		String prefix_targetString = "//";
		String suffix_targetString = "/";

		int startPos = 0;
		startPos = urlStr.indexOf(triggeringString, startPos);
		int prefixStartPos = urlStr.indexOf(prefix_targetString, startPos);
		int endPos = urlStr.indexOf(suffix_targetString, prefixStartPos + 2); // doubleQuote
		return urlStr.substring(prefixStartPos + 2, endPos);
	}

	protected abstract String extractDocTitle(String htmlbody_searchResult);

	protected abstract long extractEstimatedSize(String htmlbody_searchResult);

	protected abstract String extractDocUrl(String htmlbody_searchResult);

	protected abstract String createResult(String queryStr, int orderInResults,
			String docUrl, String docTitle, String urlDomain, String summary);

	/**
	 * 
	 * @author ISAO Saito <130s@1995.sfc.ne.jp>
	 * @since 2008/04/10
	 * @param result
	 * @param triggeringString
	 * @param prefix_targetString
	 * @param suffix_targetString
	 * @return
	 */
	protected String extractSingleString(String result,
			String triggeringString, String prefix_targetString,
			String suffix_targetString) throws IndexOutOfBoundsException
	{

		LinkedList<String> val = this.extractStringList(result,
				prefix_targetString, triggeringString, suffix_targetString);
		String str = null;
		try {
			str = (String) val.get(0);
		} catch (IndexOutOfBoundsException e) {
			// this.printElement("extractSingleString", "no value found.");
			throw e;
		}
		return str;
	}

	protected LinkedList<String> extractStringList(
			String html_searchresultpage, String prefix_targetString,
			String triggeringString, String suffix_targetString)
	{
		// System.out.println(super.getClass().getName() + " DEBUG) htm:\n"
		// + html_searchresultpage);

		LinkedList<String> values = new LinkedList<String>();
		int startPos = 0;
		while (0 <= html_searchresultpage.indexOf(triggeringString, startPos)) {
			startPos = html_searchresultpage
					.indexOf(triggeringString, startPos);
			int prefixStartPos = html_searchresultpage.indexOf(
					prefix_targetString, startPos)
					+ prefix_targetString.length();// + 1;
			int endPos = html_searchresultpage.indexOf(suffix_targetString,
					prefixStartPos); // doubleQuote
			String targetString = null;
			try {
				targetString = html_searchresultpage.substring(prefixStartPos,
						endPos);
			} catch (StringIndexOutOfBoundsException e) {
				// do nuthing for escaping exceptional string structure
			}
			// System.out.println(super.getClass().getName()
			// + " DEBUG) extracted:\n" + targetString);

			startPos++;
			values.add(targetString);
		}
		return values;
	}

	protected void printElement(String elemKey, String elemVal)
	{
		System.out.println("\t" + super.getClass().getName() + "#" + elemKey
				+ ": " + elemVal);
	}
}